package com.javaio.demo.bio.server;



import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 服务端监听客户端发起建立连接要求
 */
public class ChatServer {

    private int PROT = 8888;
    private final String TUICHU = "tuichu";
    private ServerSocket servereSocket;
    /**
     * 线程池 jdk1.8自带线程池
     */
    ExecutorService  executorService;
    /**
     * 保存的用户标识 端口/发送消息集合
     */
    private Map<Integer, Writer> connectedClinetsMap;

    public ChatServer() {
        //初始化  标识 保证不会每次只初始化一次
        connectedClinetsMap = new HashMap<>();
        //初始化线程池 newFixedThreadPool 表示最大线程只有10  FixedThreadPool 表示固定数量线程池
        executorService = Executors.newFixedThreadPool(10);
    }

    /**
     * 建立连接后将用户加入用户集合
     * synchronized 同步保证多线程中 同一时间保证只有一个线程处理保存用户
     */
    public synchronized void addClient(Socket socket) throws IOException {
        //socket里面有没有连接
        if (!Objects.equals(null, socket)) {
            //用户建立连接的端口
            int port = socket.getPort();
            //发送消息用BufferedWriter输出流
            BufferedWriter bufferedWriter = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            //保存该用户发送的消息到集合中
            connectedClinetsMap.put(port, bufferedWriter);
            System.out.println("客户端：" + port + "已连接到服务器");
        }
    }

    /**
     * 用户下线移除该用户连接避免占位
     */
    public synchronized void removeClient(Socket socket) throws IOException {
        if (!Objects.equals(null, socket)) {
            //用户建立连接的端口
            int port = socket.getPort();
            //判断该用户有没有在连接集合里面
            if (connectedClinetsMap.containsKey(port)) {
                //如果在里面 先关闭该输出流 因为BufferedWriter保存到了map集合里面  需要在集合里面关闭
                connectedClinetsMap.get(port).close();
            }
            connectedClinetsMap.remove(port);
            System.out.println("客户端：" + port + "已断开服务器");
        }
    }

    /**
     * 将该用户消息发送到所有用户实现群聊
     */
    public synchronized void forwardMessage(Socket socket, String msg) throws IOException {
        //遍历该用户集合 map则用keyset来进行遍历找到所有在线用户
        for (Integer port : connectedClinetsMap.keySet()) {
            //如果发送消息用户和在线用户集合里面的用户是同一个则没必要发送,跳过他
            if (!Objects.equals(port, socket.getPort())) {
                //通过在线用户端口key获取其他用户输出流
                Writer writer = connectedClinetsMap.get(port);
                //将该用户消息输出到其他用户流中
                writer.write(msg);
                //创建缓冲区将改writer资源放到缓冲区 避免资源消耗过多 buffered要创建缓冲区
                writer.flush();
            }
        }
    }

    /**
     * 服务端主逻辑
     */
    public void start(){
        try {
            //创建连接监听端口
            servereSocket = new ServerSocket(PROT);
            System.out.println("服务器已启动，正在监听默认端口："+PROT);
            //主线程用来不停监听客户端 用来监听客户端是否连接
            while (true){
                //如果监听到了则返回改用户socket
                Socket socket = servereSocket.accept();
                // 这里创建副线程ChatHandler用来创建客户端的对应的每一个新线程处理对应消息逻辑  一个用户socket对应一个handler线程
                //一个线程来处理一个用户  创建新线程ChatHandler用来启动（ executorService用来操作线程来进行启动该线程）
                //利用线程池来处理
                executorService.execute(new ChatHandler(this,socket));

            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("服务端servereSocket循环监听客户端socket阻塞异常");
        } finally {
          this.close();
        }
    }

    /**
     * 检查用户是否退出  退出放在服务端 是因为发起方是客户端
     */
    public boolean  readyTuiChu(String  msg){
        //这里还要判断用户发送的是否是退出消息如果是则关闭它的消息连接
        return  TUICHU.equals(msg) ? true : false;
    }

    /**
     * 关闭该服务端
     */
    public synchronized void close(){
        //关闭没用的socket的资源 避免占用资源过多消耗
        if(!Objects.equals(null,servereSocket)){
            try {
                servereSocket.close();
                System.out.println("servereSocket资源已关闭");
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("该servereSocket资源关闭连接异常");
            }
        }
    }

    public static void main(String[] args) {
        ChatServer chatServer = new ChatServer();
        chatServer.start();
    }
}